Popular Searches
Popular Course Categories
Popular Courses

Updating Objects in State

Updating Objects in State

State Management with useState

Updating Objects in State in React JS

Updating Objects in State is an important concept in React JS. Objects allow developers to store related information in a single state variable, such as a user's name, age, email, address, or profile details. When an object stored in React state needs to change, developers should create a new object instead of directly modifying the existing state object.

Learn more: JustAcademy React JS Training Course | Register for React JS Course Demo


1. What Is an Object in JavaScript?

An object is a JavaScript data structure that stores information in key-value pairs. Each key represents a property, and each property has a corresponding value.

Example

const student = {
  name: 'Rahul',
  age: 22,
  city: 'Mumbai'
};

In this example:

  • name is a property with the value 'Rahul'.
  • age is a property with the value 22.
  • city is a property with the value 'Mumbai'.

Objects are useful when several related values need to be stored together.


2. What Is State in React?

State is data that a React component remembers between renders. It can store numbers, strings, booleans, arrays, objects, and other JavaScript values.

When a component needs to remember and display changing information, the useState() Hook can be used to create state.

Example

import { useState } from 'react';
function StudentProfile() {
  const [student, setStudent] = useState({
    name: 'Rahul',
    age: 22
  });
  return (
    <div>
      <p>Name: {student.name}</p>
      <p>Age: {student.age}</p>
    </div>
  );
}
export default StudentProfile;

Here, the student state variable stores an object with two properties: name and age.


3. What Does Updating an Object in State Mean?

Updating an object in state means changing one or more of its properties and asking React to use the updated object. React state should be treated as read-only, so you should create a new object instead of directly changing the existing object.

For example, if a student's age changes from 22 to 23, you should create an updated object containing the new age.

setStudent({
  ...student,
  age: 23
});

This code copies the existing properties of student into a new object and replaces the age with 23.


4. Initializing an Object in State

Use the useState() Hook to initialize an object in a functional component.

Syntax

const [objectState, setObjectState] = useState({
  property1: value1,
  property2: value2
});

Example

import { useState } from 'react';
function UserProfile() {
  const [user, setUser] = useState({
    name: 'Priya',
    age: 25,
    city: 'Pune'
  });
  return (
    <div>
      <h2>User Profile</h2>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <p>City: {user.city}</p>
    </div>
  );
}
export default UserProfile;

The object is initialized with three properties. The component accesses each value using dot notation, such as user.name and user.city.


5. Why Should You Not Mutate State Objects Directly?

Mutation means changing the contents of an existing object instead of creating a new object.

Incorrect Example

import { useState } from 'react';
function UserProfile() {
  const [user, setUser] = useState({
    name: 'Rahul',
    age: 22
  });
  function updateAge() {
    user.age = 23;
    setUser(user);
  }
  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <button onClick={updateAge}>Update Age</button>
    </div>
  );
}
export default UserProfile;

Problem: The code changes the existing object directly and then passes the same object reference to setUser(). React may ignore the update because the new value is identical to the previous value by reference. Direct mutation can also make previous render snapshots unreliable.

Correct Example

function updateAge() {
  setUser({
    ...user,
    age: 23
  });
}

This creates a new object, preserves the other properties, and requests an update through the state setter.


6. Updating a Single Property Using the Spread Operator

The object spread operator (...) copies the enumerable own properties of an existing object into a new object. You can then override the property you want to change.

Syntax

setObjectState({
  ...objectState,
  propertyName: newValue
});

Example

import { useState } from 'react';
function Employee() {
  const [employee, setEmployee] = useState({
    name: 'Amit',
    age: 28,
    department: 'IT'
  });
  function updateDepartment() {
    setEmployee({
      ...employee,
      department: 'Development'
    });
  }
  return (
    <div>
      <p>Name: {employee.name}</p>
      <p>Age: {employee.age}</p>
      <p>Department: {employee.department}</p>
      <button onClick={updateDepartment}>
        Update Department
      </button>
    </div>
  );
}
export default Employee;

Explanation:

  • The spread operator copies the current employee properties.
  • The department property is replaced with 'Development'.
  • The employee's name and age remain unchanged.
  • React receives a new object through setEmployee().

7. Updating Multiple Properties in an Object

You can update multiple properties in the same state update by including them in the new object.

Example

import { useState } from 'react';
function Student() {
  const [student, setStudent] = useState({
    name: 'Neha',
    age: 21,
    city: 'Mumbai'
  });
  function updateStudent() {
    setStudent({
      ...student,
      age: 22,
      city: 'Nashik'
    });
  }
  return (
    <div>
      <p>Name: {student.name}</p>
      <p>Age: {student.age}</p>
      <p>City: {student.city}</p>
      <button onClick={updateStudent}>Update Student</button>
    </div>
  );
}
export default Student;

When the button is clicked, the student's age becomes 22 and the city becomes Nashik. The name remains Neha.


8. Updating State with a Functional Updater

A functional updater passes a function to the state setter. React supplies the pending previous state to that function, which returns the next state.

Syntax

setObjectState(previousState => ({
  ...previousState,
  propertyName: newValue
}));

Example

import { useState } from 'react';
function UserProfile() {
  const [user, setUser] = useState({
    name: 'Asha',
    age: 24
  });
  function increaseAge() {
    setUser(previousUser => ({
      ...previousUser,
      age: previousUser.age + 1
    }));
  }
  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <button onClick={increaseAge}>Increase Age</button>
    </div>
  );
}
export default UserProfile;

Why use this approach? The new age depends on the previous age. The functional updater calculates the next value from the pending state and is useful when updates may be queued.


9. Updating Nested Objects in State

A nested object is an object that contains another object as one of its property values. For example, a user object may contain an address object.

Example State

const [user, setUser] = useState({
  name: 'Riya',
  address: {
    city: 'Mumbai',
    country: 'India'
  }
});

To update the nested city, you must copy both the outer object and the nested address object.

Correct Update

setUser(previousUser => ({
  ...previousUser,
  address: {
    ...previousUser.address,
    city: 'Pune'
  }
}));

Explanation:

  • The first spread copies the existing user properties.
  • The second spread copies the address properties.
  • The city is replaced with Pune.
  • The name and country remain unchanged.

Important: The spread operator creates a shallow copy. It does not automatically copy nested objects, so each level along the path to the property being updated must be copied.


10. Complete Example: Updating a Nested User Profile

import { useState } from 'react';
function UserProfile() {
  const [user, setUser] = useState({
    name: 'Anjali',
    age: 25,
    address: {
      city: 'Mumbai',
      country: 'India'
    }
  });
  function updateName(event) {
    setUser(previousUser => ({
      ...previousUser,
      name: event.target.value
    }));
  }
  function updateCity(event) {
    setUser(previousUser => ({
      ...previousUser,
      address: {
        ...previousUser.address,
        city: event.target.value
      }
    }));
  }
  return (
    <div>
      <h2>User Profile</h2>
      <label>
        Name:
        <input value={user.name} onChange={updateName} />
      </label>
      <label>
        City:
        <input value={user.address.city} onChange={updateCity} />
      </label>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <p>City: {user.address.city}</p>
      <p>Country: {user.address.country}</p>
    </div>
  );
}
export default UserProfile;

This component uses controlled inputs to update the user's name and city. The functional updater ensures that each change is based on the latest pending state and preserves the other properties.


11. Updating Object Properties Dynamically

When a form contains multiple fields, you can use a single event handler to update the property corresponding to the input's name attribute.

Example

import { useState } from 'react';
function RegistrationForm() {
  const [form, setForm] = useState({
    name: '',
    email: '',
    city: ''
  });
  function handleChange(event) {
    const { name, value } = event.target;
    setForm(previousForm => ({
      ...previousForm,
      [name]: value
    }));
  }
  return (
    <form onSubmit={event => event.preventDefault()}>
      <input
        name="name"
        value={form.name}
        placeholder="Enter name"
        onChange={handleChange}
      />
      <input
        name="email"
        type="email"
        value={form.email}
        placeholder="Enter email"
        onChange={handleChange}
      />
      <input
        name="city"
        value={form.city}
        placeholder="Enter city"
        onChange={handleChange}
      />
      <p>Name: {form.name}</p>
      <p>Email: {form.email}</p>
      <p>City: {form.city}</p>
      <button type="submit">Submit</button>
    </form>
  );
}
export default RegistrationForm;

Explanation:

  • The name attribute identifies which property should be updated.
  • event.target.value provides the new input value.
  • [name] is a computed property name.
  • The spread operator preserves the other form fields.

12. Replacing an Object vs Updating a Property

The state setter replaces the state value; it does not automatically merge object properties.

Example of Replacing the Entire Object

const [user, setUser] = useState({
  name: 'Rahul',
  age: 24,
  city: 'Pune'
});
setUser({
  name: 'Priya'
});

After this update, the object contains only the name property. The previous age and city properties are not preserved.

Example of Preserving Other Properties

setUser(previousUser => ({
  ...previousUser,
  name: 'Priya'
}));

This creates a new object with the existing properties and changes only the name.


13. Shallow Copy and Deep Copy

The spread operator creates a shallow copy of an object. It copies the top-level properties, but nested objects are still referenced by the copied object unless you copy those nested objects separately.

Shallow Copy Deep Copy
Copies the top-level properties. Creates copies of nested structures as well.
Nested object references are shared. Nested objects can be independent of the originals.
Often sufficient for simple state updates. May be useful for complex structures, depending on the requirements.

Example of a Shallow Copy

const original = {
  name: 'Amit',
  address: {
    city: 'Mumbai'
  }
};
const copy = { ...original };

Although copy and original are different outer objects, their address properties refer to the same nested object. Do not mutate that nested object when it is shared with existing state.

Correct Nested Update

const updated = {
  ...original,
  address: {
    ...original.address,
    city: 'Pune'
  }
};

This creates a new outer object and a new address object while preserving the other values.


14. Updating Objects Stored Inside Arrays

Sometimes state contains an array of objects, such as a list of products or employees. To update one object, create a new array and create a new object for the item being changed.

Example

import { useState } from 'react';
function ShoppingCart() {
  const [products, setProducts] = useState([
    { id: 1, name: 'Keyboard', quantity: 1 },
    { id: 2, name: 'Mouse', quantity: 2 }
  ]);
  function increaseQuantity(productId) {
    setProducts(previousProducts =>
      previousProducts.map(product =>
        product.id === productId
          ? { ...product, quantity: product.quantity + 1 }
          : product
      )
    );
  }
  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}: {product.quantity}
          <button onClick={() => increaseQuantity(product.id)}>
            Increase
          </button>
        </li>
      ))}
    </ul>
  );
}
export default ShoppingCart;

The map() method creates a new array. The selected product is replaced with a new object containing an increased quantity, while the other products remain unchanged.


15. Common Mistakes When Updating Objects

Mistake 1: Directly changing a property

// Incorrect
user.name = 'Neha';
setUser(user);

Correct:

setUser(previousUser => ({
  ...previousUser,
  name: 'Neha'
}));

Mistake 2: Forgetting the spread operator

// May remove other properties
setUser({ age: 30 });

Correct when changing only the age:

setUser(previousUser => ({
  ...previousUser,
  age: 30
}));

Mistake 3: Copying only the outer object

// Incorrect: mutates the existing nested address
setUser(previousUser => {
  const nextUser = { ...previousUser };
  nextUser.address.city = 'Pune';
  return nextUser;
});

Correct:

setUser(previousUser => ({
  ...previousUser,
  address: {
    ...previousUser.address,
    city: 'Pune'
  }
}));

Mistake 4: Assuming the state changes immediately

Calling a setter requests an update. The state variable in the currently running render still contains the value from that render. React provides the updated state during a subsequent render.

Mistake 5: Using a stale object for updates

When the next value depends on the previous state, use the functional updater form:

setUser(previousUser => ({
  ...previousUser,
  age: previousUser.age + 1
}));

16. Best Practices for Updating Objects in State

  • Treat state objects as read-only.
  • Use the state setter to request updates.
  • Use the spread operator to preserve existing properties.
  • Copy every level of a nested object that you need to update.
  • Use functional updaters when the next object depends on the previous state.
  • Use a single object state for related fields when it makes the code easier to maintain.
  • Use separate state variables when the values are independent and separate management is clearer.
  • Keep state structures as simple as practical.
  • Avoid creating redundant state that can be calculated from existing state.
  • Consider libraries such as Immer if deeply nested updates become repetitive.

17. Optional Technique: Updating Objects with Immer

Immer is a library that helps simplify immutable updates, especially when state contains deeply nested objects. It lets you write code that resembles mutation while generating updated immutable state internally.

Install the required packages:

npm install use-immer

Example

import { useImmer } from 'use-immer';
function UserProfile() {
  const [user, updateUser] = useImmer({
    name: 'Riya',
    address: {
      city: 'Mumbai'
    }
  });
  function changeCity() {
    updateUser(draft => {
      draft.address.city = 'Pune';
    });
  }
  return (
    <div>
      <p>Name: {user.name}</p>
      <p>City: {user.address.city}</p>
      <button onClick={changeCity}>Change City</button>
    </div>
  );
}
export default UserProfile;

Immer manages the immutable update process. The draft can be changed inside the updater, but do not use this mutation-style approach with ordinary useState() objects.


18. Practical Project: Employee Profile Manager

This mini-project demonstrates updating object properties, including a nested address, through controlled form inputs.

import { useState } from 'react';
function EmployeeProfile() {
  const [employee, setEmployee] = useState({
    name: 'Amit',
    role: 'Developer',
    address: {
      city: 'Mumbai',
      country: 'India'
    }
  });
  function handleNameChange(event) {
    setEmployee(previousEmployee => ({
      ...previousEmployee,
      name: event.target.value
    }));
  }
  function handleRoleChange(event) {
    setEmployee(previousEmployee => ({
      ...previousEmployee,
      role: event.target.value
    }));
  }
  function handleCityChange(event) {
    setEmployee(previousEmployee => ({
      ...previousEmployee,
      address: {
        ...previousEmployee.address,
        city: event.target.value
      }
    }));
  }
  return (
    <div>
      <h2>Employee Profile Manager</h2>
      <label>
        Name:
        <input value={employee.name} onChange={handleNameChange} />
      </label>
      <label>
        Role:
        <input value={employee.role} onChange={handleRoleChange} />
      </label>
      <label>
        City:
        <input value={employee.address.city} onChange={handleCityChange} />
      </label>
      <h3>Updated Details</h3>
      <p>Name: {employee.name}</p>
      <p>Role: {employee.role}</p>
      <p>City: {employee.address.city}</p>
      <p>Country: {employee.address.country}</p>
    </div>
  );
}
export default EmployeeProfile;

Project Explanation

  • The employee state stores an object containing employee details.
  • The name and role handlers create new objects with updated values.
  • The city handler copies both the employee object and nested address object.
  • The other properties are preserved during each update.
  • The displayed profile reflects the current state.

19. Interview Questions and Answers

Q1. What does updating an object in React state mean?

It means creating a new object with the required changes and passing it to the state setter.

Q2. Why should state objects not be mutated directly?

Direct mutation changes existing state without correctly communicating the update to React and can affect data associated with previous renders.

Q3. What is the purpose of the spread operator?

The spread operator copies an object's enumerable own properties into a new object, allowing selected properties to be replaced.

Q4. Does React automatically merge object state updates?

No. The setter returned by useState() replaces the state value. Use the spread operator when you need to preserve existing properties.

Q5. How do you update a nested object?

Create a new object at each level between the outer state object and the property being changed.

Q6. What is a functional updater?

A function passed to the state setter that receives the pending previous state and returns the next state.

Q7. What is the difference between a shallow copy and a deep copy?

A shallow copy copies the top-level properties while retaining references to nested objects. A deep copy duplicates nested structures as well.

Q8. How can you update one object inside an array?

Use map() to create a new array and the spread operator to create a new version of the object being changed.

Q9. Can you update several object properties in one setter call?

Yes. Create a new object, copy the existing properties, and specify all the new property values.

Q10. What is Immer used for?

Immer helps create immutable state updates with concise syntax, especially for deeply nested objects and arrays.


20. Practice Exercises

  1. Create a student object with name, age, and city. Update only the age.
  2. Create an employee profile and update the employee's role.
  3. Create a nested address object and change its city while preserving the country.
  4. Build a form that updates name, email, and phone using one event handler.
  5. Create a product object and update its price without changing its name.
  6. Build a shopping cart that updates the quantity of one selected product.
  7. Write a functional updater that increases an object's score by five.
  8. Experiment with the spread operator to understand shallow copies and nested references.

21. Quick Revision Checklist

  • Understand JavaScript objects and key-value pairs.
  • Know how to initialize an object with useState().
  • Understand why state objects should not be mutated directly.
  • Use the spread operator to create updated objects.
  • Preserve existing properties when changing one field.
  • Copy each required level when updating nested objects.
  • Use functional updaters when updates depend on previous state.
  • Know how to update objects inside arrays.
  • Understand shallow copies and nested object references.
  • Practice controlled form inputs and object updates.

22. Useful Resources


23. Conclusion

Updating objects in state is a fundamental React JS skill. The recommended approach is to treat state as read-only, create a new object with the required changes, and pass it to the state setter. The spread operator helps preserve existing properties, while nested objects require copying each level that is updated. Functional updater functions are useful when changes depend on previous state. These practices make React components more predictable, maintainable, and easier to debug.

To learn more, visit JustAcademy React JS Training Course or register for a React JS course demo.

whatsapp